<template>
  <div>
    <!-- 19 增加全屏按钮 -->
    <!-- 20 用控制面板控制全屏
       <button class="full-screen-btn" @click="onClickFullScreen">全屏</button> 
      -->
  </div>
</template>

<script>
import * as THREE from "three";
// 导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
// 20 导入gui控制面板
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";

// 导入HDR加载器
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader";

// GLTFLoader
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";

// 24.1 导入顶点法向量辅助器
import { VertexNormalsHelper } from "three/examples/jsm/helpers/VertexNormalsHelper";

/**
 * 值类型
 * - Vector3
 * (x,y,z)
 * - Euler 欧拉: 描述一个旋转变换,通过指定轴顺序和其各个轴向上的指定旋转角度来旋转一个物体
 * (x,y,z,order) - order: 'XYZ' or 'YXZ' or 'ZXY' or 'ZYX',
 */

export default {
  data() {
    return {};
  },
  methods: {
    // 19 增加全屏按钮
    onClickFullScreen() {
      // 渲染器画布全屏
      window.renderer && window.renderer.domElement.requestFullscreen();
      // 浏览器窗口全屏
      // document.body.requestFullscreen()
      // 退出全屏
      // document.exitFullscreen()
    },
  },
  mounted() {
    // 1 创建场景
    const scene = new THREE.Scene();

    // 2 创建相机
    const camera = new THREE.PerspectiveCamera(
      75,
      window.innerWidth / window.innerHeight,
      0.1,
      1000,
    );
    camera.position.z = 5;
    camera.position.x = 5;
    camera.position.y = 5;
    // 3 相机指向原点
    camera.lookAt(new THREE.Vector3(0, 0, 0));

    // 4 创建一个渲染器 canvas
    const renderer = new THREE.WebGLRenderer({
      // 4.1.1 设置渲染器锯齿属性
      antialias: true,
    });
    // 4.1.2 属性设置
    // renderer.antialias = true
    renderer.setSize(window.innerWidth, window.innerHeight);

    // 25.1 开启渲染器的阴影贴图
    renderer.shadowMap.enabled = true;

    document.body.appendChild(renderer.domElement);

    // 8 添加轨道控制器
    const controls = new OrbitControls(camera, renderer.domElement); // 创建轨道控制器, 第二参数表示监听事件的DOM
    // const controls = new OrbitControls(camera, document.body); // 创建轨道控制器, 第二参数表示监听事件的DOM, 监听了body后, OrbitControls会拦截所有的事件
    // 9 设置阻尼
    controls.enableDamping = true;
    controls.dampingFactor = 0.1; // 设置阻尼系数, 数值越小, 惯性越大, 数值越大, 停下来越快

    // 10 自动旋转
    controls.autoRotate = false; // 自动旋转

    // 7 添加世界坐标辅助器 x轴向右红色r y轴向上绿色g z轴向前蓝b
    const axesHelper = new THREE.AxesHelper(5); // 辅助线长度为5
    scene.add(axesHelper);

    // 6 动作绘制
    function animate() {
      /* 8 添加轨道控制器 根据控制器更新画面 */
      controls.update();
      requestAnimationFrame(animate); // 请求浏览器重新绘制画面
      /* 自动旋转
      cube.rotation.x += 0.01; // x轴旋转速度增加
      cube.rotation.y += 0.01; // y轴旋转速度增加
      */
      renderer.render(scene, camera); // 渲染场景和相机设置到屏幕上
    }
    animate(); // 开始动画循环

    // 18 监听全屏事件, 让画布适配屏幕尺寸
    window.addEventListener("resize", function() {
      // 重置渲染器宽高比
      renderer.setSize(window.innerWidth, window.innerHeight);
      // 4.2 设置设备像素比
      renderer.setPixelRatio(window.devicePixelRatio);
      // 重置相机宽高比
      camera.aspect = window.innerWidth / window.innerHeight;
      // 更新相机投影矩阵
      camera.updateProjectionMatrix();
    });
    /** 20 用GUI控制面板控制全屏
    // 19 增加全屏按钮
    window.scene = scene;
    window.camera = camera;
    window.renderer = renderer;
    window.controls = controls;
    */
    // 20 GUI控制面板
    let eventObj = {
      Fullscreen: function() {
        document.body.requestFullscreen();
      },
      ExitFullscreen: function() {
        document.exitFullscreen();
      },
    };
    const gui = new GUI();
    // 第一个参数是事件对象, 第二个参数是该事件对象的key
    gui.add(eventObj, "Fullscreen").name("全屏");
    gui.add(eventObj, "ExitFullscreen").name("退出全屏");

    // 创建一个球几何
    const sphereGeometry = new THREE.SphereBufferGeometry(1, 20, 20);
    // 使用标准网格材质
    const sphereMaterial = new THREE.MeshStandardMaterial({
      color: 0xffffff,
    });
    const sphereMesh = new THREE.Mesh(sphereGeometry, sphereMaterial);
    // 25.3 开启物体的阴影投射
    sphereMesh.castShadow = true;
    scene.add(sphereMesh);

    // 21 打光 - 环境光会均匀的照亮场景中的所有物体。不能投射阴影
    const sceneLight = new THREE.AmbientLight(0xffffff, 0.5); // soft white light
    scene.add(sceneLight);

    // 22 聚光灯 白光 强度0.5 - https://www.bilibili.com/video/BV1Gg411X7FY?p=31
    const spotLight = new THREE.SpotLight(0xffffff, 0.5);
    // 22.1 设置位置
    spotLight.position.set(10, 10, 10);
    // 25.2.1 开启灯光投射阴影,默认false
    spotLight.castShadow = true;
    spotLight.angle = 0.39;
    spotLight.intensity = 0.9;
    spotLight.distance = 90;
    spotLight.penumbra = 0.1;
    spotLight.decay = 2;

    // 25.2.2 设置模糊阴影的边缘。默认1
    spotLight.shadow.radius = 5;
    // 25.2.3 设置阴影的分辨率 默认(512,512)
    spotLight.shadow.mapSize.set(1024, 1024);
    // 25.2.4 设置平行光投射相机的属性
    spotLight.shadow.camera.near = 0.5;
    spotLight.shadow.camera.far = 100;
    // 25.2.4 设置灯光照射对象
    spotLight.target = sphereMesh

    scene.add(spotLight);

    // 26 增加灯光辅助器
    const lightHelper = new THREE.SpotLightHelper(spotLight, 0xffffff);
    scene.add(lightHelper);

    // 25.2.5 改变球体的位置
    gui
      .add(sphereMesh.position, "x")
      .min(-20)
      .max(20)
      .step(0.1)
      .name('球X轴位置').onChange(() => {
      lightHelper.update()
    });
    // 25.2.6 用GUI更新光投射相机的属性
    let lightFolder = gui.addFolder('光源属性')
    lightFolder.open()
    lightFolder.add(spotLight, 'angle').min(0).max(Math.PI/2).step(0.01).name('照射范围').onChange(() => {
      lightHelper.update()
    })
    lightFolder.add(spotLight, 'intensity').min(0).max(1).step(0.01).name('强度').onChange(() => {
      lightHelper.update()
    })
    lightFolder.add(spotLight, 'distance').min(0).max(500).step(10).name('最大距离').onChange(() => {
      lightHelper.update()
    })
    lightFolder.add(spotLight, 'penumbra').min(0).max(1).step(0.1).name('半影衰减').onChange(() => {
      lightHelper.update()
    })
    lightFolder.add(spotLight, 'decay').min(0).max(10).step(1).name('衰减量').onChange(() => {
      lightHelper.update()
    })


    // 23 创建一个平面
    const planeGeometry = new THREE.PlaneBufferGeometry(50, 50);
    const planeMesh = new THREE.Mesh(planeGeometry, sphereMaterial); // 跟球同样材质
    planeMesh.rotation.x = -Math.PI / 2; // 水平
    planeMesh.position.set(0, -1, 0);
    // 25.4 平面接受阴影
    planeMesh.receiveShadow = true;
    scene.add(planeMesh);

    // 25 添加阴影 - 需要点灯光或者平行灯光
    /**
     * 1. 材质满足对光照有反应
     * 2. 渲染器开启阴影的计算
     * 3. 光照投射阴影
     * 4. 物体投射阴影
     * 5. 平面物体接收阴影
     */
  },
};
</script>

<style>
* {
  margin: 0;
  padding: 0;
}
body {
  height: 100vh;
  width: 100vw;
  overflow: hidden;
}

.full-screen-btn {
  position: fixed;
  left: 2vw;
  top: 2vh;
  z-index: 2;
}
</style>
